home *** CD-ROM | disk | FTP | other *** search
/ PC World Komputer 2010 April / PCWorld0410.iso / hity wydania / Ubuntu 9.10 PL / karmelkowy-koliberek-desktop-9.10-i386-PL.iso / casper / filesystem.squashfs / usr / lib / python2.6 / SimpleXMLRPCServer.py < prev    next >
Text File  |  2009-11-02  |  22KB  |  616 lines

  1. """Simple XML-RPC Server.
  2.  
  3. This module can be used to create simple XML-RPC servers
  4. by creating a server and either installing functions, a
  5. class instance, or by extending the SimpleXMLRPCServer
  6. class.
  7.  
  8. It can also be used to handle XML-RPC requests in a CGI
  9. environment using CGIXMLRPCRequestHandler.
  10.  
  11. A list of possible usage patterns follows:
  12.  
  13. 1. Install functions:
  14.  
  15. server = SimpleXMLRPCServer(("localhost", 8000))
  16. server.register_function(pow)
  17. server.register_function(lambda x,y: x+y, 'add')
  18. server.serve_forever()
  19.  
  20. 2. Install an instance:
  21.  
  22. class MyFuncs:
  23.     def __init__(self):
  24.         # make all of the string functions available through
  25.         # string.func_name
  26.         import string
  27.         self.string = string
  28.     def _listMethods(self):
  29.         # implement this method so that system.listMethods
  30.         # knows to advertise the strings methods
  31.         return list_public_methods(self) + \
  32.                 ['string.' + method for method in list_public_methods(self.string)]
  33.     def pow(self, x, y): return pow(x, y)
  34.     def add(self, x, y) : return x + y
  35.  
  36. server = SimpleXMLRPCServer(("localhost", 8000))
  37. server.register_introspection_functions()
  38. server.register_instance(MyFuncs())
  39. server.serve_forever()
  40.  
  41. 3. Install an instance with custom dispatch method:
  42.  
  43. class Math:
  44.     def _listMethods(self):
  45.         # this method must be present for system.listMethods
  46.         # to work
  47.         return ['add', 'pow']
  48.     def _methodHelp(self, method):
  49.         # this method must be present for system.methodHelp
  50.         # to work
  51.         if method == 'add':
  52.             return "add(2,3) => 5"
  53.         elif method == 'pow':
  54.             return "pow(x, y[, z]) => number"
  55.         else:
  56.             # By convention, return empty
  57.             # string if no help is available
  58.             return ""
  59.     def _dispatch(self, method, params):
  60.         if method == 'pow':
  61.             return pow(*params)
  62.         elif method == 'add':
  63.             return params[0] + params[1]
  64.         else:
  65.             raise 'bad method'
  66.  
  67. server = SimpleXMLRPCServer(("localhost", 8000))
  68. server.register_introspection_functions()
  69. server.register_instance(Math())
  70. server.serve_forever()
  71.  
  72. 4. Subclass SimpleXMLRPCServer:
  73.  
  74. class MathServer(SimpleXMLRPCServer):
  75.     def _dispatch(self, method, params):
  76.         try:
  77.             # We are forcing the 'export_' prefix on methods that are
  78.             # callable through XML-RPC to prevent potential security
  79.             # problems
  80.             func = getattr(self, 'export_' + method)
  81.         except AttributeError:
  82.             raise Exception('method "%s" is not supported' % method)
  83.         else:
  84.             return func(*params)
  85.  
  86.     def export_add(self, x, y):
  87.         return x + y
  88.  
  89. server = MathServer(("localhost", 8000))
  90. server.serve_forever()
  91.  
  92. 5. CGI script:
  93.  
  94. server = CGIXMLRPCRequestHandler()
  95. server.register_function(pow)
  96. server.handle_request()
  97. """
  98.  
  99. # Written by Brian Quinlan (brian@sweetapp.com).
  100. # Based on code written by Fredrik Lundh.
  101.  
  102. import xmlrpclib
  103. from xmlrpclib import Fault
  104. import SocketServer
  105. import BaseHTTPServer
  106. import sys
  107. import os
  108. import traceback
  109. try:
  110.     import fcntl
  111. except ImportError:
  112.     fcntl = None
  113.  
  114. def resolve_dotted_attribute(obj, attr, allow_dotted_names=True):
  115.     """resolve_dotted_attribute(a, 'b.c.d') => a.b.c.d
  116.  
  117.     Resolves a dotted attribute name to an object.  Raises
  118.     an AttributeError if any attribute in the chain starts with a '_'.
  119.  
  120.     If the optional allow_dotted_names argument is false, dots are not
  121.     supported and this function operates similar to getattr(obj, attr).
  122.     """
  123.  
  124.     if allow_dotted_names:
  125.         attrs = attr.split('.')
  126.     else:
  127.         attrs = [attr]
  128.  
  129.     for i in attrs:
  130.         if i.startswith('_'):
  131.             raise AttributeError(
  132.                 'attempt to access private attribute "%s"' % i
  133.                 )
  134.         else:
  135.             obj = getattr(obj,i)
  136.     return obj
  137.  
  138. def list_public_methods(obj):
  139.     """Returns a list of attribute strings, found in the specified
  140.     object, which represent callable attributes"""
  141.  
  142.     return [member for member in dir(obj)
  143.                 if not member.startswith('_') and
  144.                     hasattr(getattr(obj, member), '__call__')]
  145.  
  146. def remove_duplicates(lst):
  147.     """remove_duplicates([2,2,2,1,3,3]) => [3,1,2]
  148.  
  149.     Returns a copy of a list without duplicates. Every list
  150.     item must be hashable and the order of the items in the
  151.     resulting list is not defined.
  152.     """
  153.     u = {}
  154.     for x in lst:
  155.         u[x] = 1
  156.  
  157.     return u.keys()
  158.  
  159. class SimpleXMLRPCDispatcher:
  160.     """Mix-in class that dispatches XML-RPC requests.
  161.  
  162.     This class is used to register XML-RPC method handlers
  163.     and then to dispatch them. There should never be any
  164.     reason to instantiate this class directly.
  165.     """
  166.  
  167.     def __init__(self, allow_none=False, encoding=None):
  168.         self.funcs = {}
  169.         self.instance = None
  170.         self.allow_none = allow_none
  171.         self.encoding = encoding
  172.  
  173.     def register_instance(self, instance, allow_dotted_names=False):
  174.         """Registers an instance to respond to XML-RPC requests.
  175.  
  176.         Only one instance can be installed at a time.
  177.  
  178.         If the registered instance has a _dispatch method then that
  179.         method will be called with the name of the XML-RPC method and
  180.         its parameters as a tuple
  181.         e.g. instance._dispatch('add',(2,3))
  182.  
  183.         If the registered instance does not have a _dispatch method
  184.         then the instance will be searched to find a matching method
  185.         and, if found, will be called. Methods beginning with an '_'
  186.         are considered private and will not be called by
  187.         SimpleXMLRPCServer.
  188.  
  189.         If a registered function matches a XML-RPC request, then it
  190.         will be called instead of the registered instance.
  191.  
  192.         If the optional allow_dotted_names argument is true and the
  193.         instance does not have a _dispatch method, method names
  194.         containing dots are supported and resolved, as long as none of
  195.         the name segments start with an '_'.
  196.  
  197.             *** SECURITY WARNING: ***
  198.  
  199.             Enabling the allow_dotted_names options allows intruders
  200.             to access your module's global variables and may allow
  201.             intruders to execute arbitrary code on your machine.  Only
  202.             use this option on a secure, closed network.
  203.  
  204.         """
  205.  
  206.         self.instance = instance
  207.         self.allow_dotted_names = allow_dotted_names
  208.  
  209.     def register_function(self, function, name = None):
  210.         """Registers a function to respond to XML-RPC requests.
  211.  
  212.         The optional name argument can be used to set a Unicode name
  213.         for the function.
  214.         """
  215.  
  216.         if name is None:
  217.             name = function.__name__
  218.         self.funcs[name] = function
  219.  
  220.     def register_introspection_functions(self):
  221.         """Registers the XML-RPC introspection methods in the system
  222.         namespace.
  223.  
  224.         see http://xmlrpc.usefulinc.com/doc/reserved.html
  225.         """
  226.  
  227.         self.funcs.update({'system.listMethods' : self.system_listMethods,
  228.                       'system.methodSignature' : self.system_methodSignature,
  229.                       'system.methodHelp' : self.system_methodHelp})
  230.  
  231.     def register_multicall_functions(self):
  232.         """Registers the XML-RPC multicall method in the system
  233.         namespace.
  234.  
  235.         see http://www.xmlrpc.com/discuss/msgReader$1208"""
  236.  
  237.         self.funcs.update({'system.multicall' : self.system_multicall})
  238.  
  239.     def _marshaled_dispatch(self, data, dispatch_method = None):
  240.         """Dispatches an XML-RPC method from marshalled (XML) data.
  241.  
  242.         XML-RPC methods are dispatched from the marshalled (XML) data
  243.         using the _dispatch method and the result is returned as
  244.         marshalled data. For backwards compatibility, a dispatch
  245.         function can be provided as an argument (see comment in
  246.         SimpleXMLRPCRequestHandler.do_POST) but overriding the
  247.         existing method through subclassing is the prefered means
  248.         of changing method dispatch behavior.
  249.         """
  250.  
  251.         try:
  252.             params, method = xmlrpclib.loads(data)
  253.  
  254.             # generate response
  255.             if dispatch_method is not None:
  256.                 response = dispatch_method(method, params)
  257.             else:
  258.                 response = self._dispatch(method, params)
  259.             # wrap response in a singleton tuple
  260.             response = (response,)
  261.             response = xmlrpclib.dumps(response, methodresponse=1,
  262.                                        allow_none=self.allow_none, encoding=self.encoding)
  263.         except Fault, fault:
  264.             response = xmlrpclib.dumps(fault, allow_none=self.allow_none,
  265.                                        encoding=self.encoding)
  266.         except:
  267.             # report exception back to server
  268.             exc_type, exc_value, exc_tb = sys.exc_info()
  269.             response = xmlrpclib.dumps(
  270.                 xmlrpclib.Fault(1, "%s:%s" % (exc_type, exc_value)),
  271.                 encoding=self.encoding, allow_none=self.allow_none,
  272.                 )
  273.  
  274.         return response
  275.  
  276.     def system_listMethods(self):
  277.         """system.listMethods() => ['add', 'subtract', 'multiple']
  278.  
  279.         Returns a list of the methods supported by the server."""
  280.  
  281.         methods = self.funcs.keys()
  282.         if self.instance is not None:
  283.             # Instance can implement _listMethod to return a list of
  284.             # methods
  285.             if hasattr(self.instance, '_listMethods'):
  286.                 methods = remove_duplicates(
  287.                         methods + self.instance._listMethods()
  288.                     )
  289.             # if the instance has a _dispatch method then we
  290.             # don't have enough information to provide a list
  291.             # of methods
  292.             elif not hasattr(self.instance, '_dispatch'):
  293.                 methods = remove_duplicates(
  294.                         methods + list_public_methods(self.instance)
  295.                     )
  296.         methods.sort()
  297.         return methods
  298.  
  299.     def system_methodSignature(self, method_name):
  300.         """system.methodSignature('add') => [double, int, int]
  301.  
  302.         Returns a list describing the signature of the method. In the
  303.         above example, the add method takes two integers as arguments
  304.         and returns a double result.
  305.  
  306.         This server does NOT support system.methodSignature."""
  307.  
  308.         # See http://xmlrpc.usefulinc.com/doc/sysmethodsig.html
  309.  
  310.         return 'signatures not supported'
  311.  
  312.     def system_methodHelp(self, method_name):
  313.         """system.methodHelp('add') => "Adds two integers together"
  314.  
  315.         Returns a string containing documentation for the specified method."""
  316.  
  317.         method = None
  318.         if method_name in self.funcs:
  319.             method = self.funcs[method_name]
  320.         elif self.instance is not None:
  321.             # Instance can implement _methodHelp to return help for a method
  322.             if hasattr(self.instance, '_methodHelp'):
  323.                 return self.instance._methodHelp(method_name)
  324.             # if the instance has a _dispatch method then we
  325.             # don't have enough information to provide help
  326.             elif not hasattr(self.instance, '_dispatch'):
  327.                 try:
  328.                     method = resolve_dotted_attribute(
  329.                                 self.instance,
  330.                                 method_name,
  331.                                 self.allow_dotted_names
  332.                                 )
  333.                 except AttributeError:
  334.                     pass
  335.  
  336.         # Note that we aren't checking that the method actually
  337.         # be a callable object of some kind
  338.         if method is None:
  339.             return ""
  340.         else:
  341.             import pydoc
  342.             return pydoc.getdoc(method)
  343.  
  344.     def system_multicall(self, call_list):
  345.         """system.multicall([{'methodName': 'add', 'params': [2, 2]}, ...]) => \
  346. [[4], ...]
  347.  
  348.         Allows the caller to package multiple XML-RPC calls into a single
  349.         request.
  350.  
  351.         See http://www.xmlrpc.com/discuss/msgReader$1208
  352.         """
  353.  
  354.         results = []
  355.         for call in call_list:
  356.             method_name = call['methodName']
  357.             params = call['params']
  358.  
  359.             try:
  360.                 # XXX A marshalling error in any response will fail the entire
  361.                 # multicall. If someone cares they should fix this.
  362.                 results.append([self._dispatch(method_name, params)])
  363.             except Fault, fault:
  364.                 results.append(
  365.                     {'faultCode' : fault.faultCode,
  366.                      'faultString' : fault.faultString}
  367.                     )
  368.             except:
  369.                 exc_type, exc_value, exc_tb = sys.exc_info()
  370.                 results.append(
  371.                     {'faultCode' : 1,
  372.                      'faultString' : "%s:%s" % (exc_type, exc_value)}
  373.                     )
  374.         return results
  375.  
  376.     def _dispatch(self, method, params):
  377.         """Dispatches the XML-RPC method.
  378.  
  379.         XML-RPC calls are forwarded to a registered function that
  380.         matches the called XML-RPC method name. If no such function
  381.         exists then the call is forwarded to the registered instance,
  382.         if available.
  383.  
  384.         If the registered instance has a _dispatch method then that
  385.         method will be called with the name of the XML-RPC method and
  386.         its parameters as a tuple
  387.         e.g. instance._dispatch('add',(2,3))
  388.  
  389.         If the registered instance does not have a _dispatch method
  390.         then the instance will be searched to find a matching method
  391.         and, if found, will be called.
  392.  
  393.         Methods beginning with an '_' are considered private and will
  394.         not be called.
  395.         """
  396.  
  397.         func = None
  398.         try:
  399.             # check to see if a matching function has been registered
  400.             func = self.funcs[method]
  401.         except KeyError:
  402.             if self.instance is not None:
  403.                 # check for a _dispatch method
  404.                 if hasattr(self.instance, '_dispatch'):
  405.                     return self.instance._dispatch(method, params)
  406.                 else:
  407.                     # call instance method directly
  408.                     try:
  409.                         func = resolve_dotted_attribute(
  410.                             self.instance,
  411.                             method,
  412.                             self.allow_dotted_names
  413.                             )
  414.                     except AttributeError:
  415.                         pass
  416.  
  417.         if func is not None:
  418.             return func(*params)
  419.         else:
  420.             raise Exception('method "%s" is not supported' % method)
  421.  
  422. class SimpleXMLRPCRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler):
  423.     """Simple XML-RPC request handler class.
  424.  
  425.     Handles all HTTP POST requests and attempts to decode them as
  426.     XML-RPC requests.
  427.     """
  428.  
  429.     # Class attribute listing the accessible path components;
  430.     # paths not on this list will result in a 404 error.
  431.     rpc_paths = ('/', '/RPC2')
  432.  
  433.     def is_rpc_path_valid(self):
  434.         if self.rpc_paths:
  435.             return self.path in self.rpc_paths
  436.         else:
  437.             # If .rpc_paths is empty, just assume all paths are legal
  438.             return True
  439.  
  440.     def do_POST(self):
  441.         """Handles the HTTP POST request.
  442.  
  443.         Attempts to interpret all HTTP POST requests as XML-RPC calls,
  444.         which are forwarded to the server's _dispatch method for handling.
  445.         """
  446.  
  447.         # Check that the path is legal
  448.         if not self.is_rpc_path_valid():
  449.             self.report_404()
  450.             return
  451.  
  452.         try:
  453.             # Get arguments by reading body of request.
  454.             # We read this in chunks to avoid straining
  455.             # socket.read(); around the 10 or 15Mb mark, some platforms
  456.             # begin to have problems (bug #792570).
  457.             max_chunk_size = 10*1024*1024
  458.             size_remaining = int(self.headers["content-length"])
  459.             L = []
  460.             while size_remaining:
  461.                 chunk_size = min(size_remaining, max_chunk_size)
  462.                 L.append(self.rfile.read(chunk_size))
  463.                 size_remaining -= len(L[-1])
  464.             data = ''.join(L)
  465.  
  466.             # In previous versions of SimpleXMLRPCServer, _dispatch
  467.             # could be overridden in this class, instead of in
  468.             # SimpleXMLRPCDispatcher. To maintain backwards compatibility,
  469.             # check to see if a subclass implements _dispatch and dispatch
  470.             # using that method if present.
  471.             response = self.server._marshaled_dispatch(
  472.                     data, getattr(self, '_dispatch', None)
  473.                 )
  474.         except Exception, e: # This should only happen if the module is buggy
  475.             # internal error, report as HTTP server error
  476.             self.send_response(500)
  477.  
  478.             # Send information about the exception if requested
  479.             if hasattr(self.server, '_send_traceback_header') and \
  480.                     self.server._send_traceback_header:
  481.                 self.send_header("X-exception", str(e))
  482.                 self.send_header("X-traceback", traceback.format_exc())
  483.  
  484.             self.end_headers()
  485.         else:
  486.             # got a valid XML RPC response
  487.             self.send_response(200)
  488.             self.send_header("Content-type", "text/xml")
  489.             self.send_header("Content-length", str(len(response)))
  490.             self.end_headers()
  491.             self.wfile.write(response)
  492.  
  493.             # shut down the connection
  494.             self.wfile.flush()
  495.             self.connection.shutdown(1)
  496.  
  497.     def report_404 (self):
  498.             # Report a 404 error
  499.         self.send_response(404)
  500.         response = 'No such page'
  501.         self.send_header("Content-type", "text/plain")
  502.         self.send_header("Content-length", str(len(response)))
  503.         self.end_headers()
  504.         self.wfile.write(response)
  505.         # shut down the connection
  506.         self.wfile.flush()
  507.         self.connection.shutdown(1)
  508.  
  509.     def log_request(self, code='-', size='-'):
  510.         """Selectively log an accepted request."""
  511.  
  512.         if self.server.logRequests:
  513.             BaseHTTPServer.BaseHTTPRequestHandler.log_request(self, code, size)
  514.  
  515. class SimpleXMLRPCServer(SocketServer.TCPServer,
  516.                          SimpleXMLRPCDispatcher):
  517.     """Simple XML-RPC server.
  518.  
  519.     Simple XML-RPC server that allows functions and a single instance
  520.     to be installed to handle requests. The default implementation
  521.     attempts to dispatch XML-RPC calls to the functions or instance
  522.     installed in the server. Override the _dispatch method inhereted
  523.     from SimpleXMLRPCDispatcher to change this behavior.
  524.     """
  525.  
  526.     allow_reuse_address = True
  527.  
  528.     # Warning: this is for debugging purposes only! Never set this to True in
  529.     # production code, as will be sending out sensitive information (exception
  530.     # and stack trace details) when exceptions are raised inside
  531.     # SimpleXMLRPCRequestHandler.do_POST
  532.     _send_traceback_header = False
  533.  
  534.     def __init__(self, addr, requestHandler=SimpleXMLRPCRequestHandler,
  535.                  logRequests=True, allow_none=False, encoding=None, bind_and_activate=True):
  536.         self.logRequests = logRequests
  537.  
  538.         SimpleXMLRPCDispatcher.__init__(self, allow_none, encoding)
  539.         SocketServer.TCPServer.__init__(self, addr, requestHandler, bind_and_activate)
  540.  
  541.         # [Bug #1222790] If possible, set close-on-exec flag; if a
  542.         # method spawns a subprocess, the subprocess shouldn't have
  543.         # the listening socket open.
  544.         if fcntl is not None and hasattr(fcntl, 'FD_CLOEXEC'):
  545.             flags = fcntl.fcntl(self.fileno(), fcntl.F_GETFD)
  546.             flags |= fcntl.FD_CLOEXEC
  547.             fcntl.fcntl(self.fileno(), fcntl.F_SETFD, flags)
  548.  
  549. class CGIXMLRPCRequestHandler(SimpleXMLRPCDispatcher):
  550.     """Simple handler for XML-RPC data passed through CGI."""
  551.  
  552.     def __init__(self, allow_none=False, encoding=None):
  553.         SimpleXMLRPCDispatcher.__init__(self, allow_none, encoding)
  554.  
  555.     def handle_xmlrpc(self, request_text):
  556.         """Handle a single XML-RPC request"""
  557.  
  558.         response = self._marshaled_dispatch(request_text)
  559.  
  560.         print 'Content-Type: text/xml'
  561.         print 'Content-Length: %d' % len(response)
  562.         print
  563.         sys.stdout.write(response)
  564.  
  565.     def handle_get(self):
  566.         """Handle a single HTTP GET request.
  567.  
  568.         Default implementation indicates an error because
  569.         XML-RPC uses the POST method.
  570.         """
  571.  
  572.         code = 400
  573.         message, explain = \
  574.                  BaseHTTPServer.BaseHTTPRequestHandler.responses[code]
  575.  
  576.         response = BaseHTTPServer.DEFAULT_ERROR_MESSAGE % \
  577.             {
  578.              'code' : code,
  579.              'message' : message,
  580.              'explain' : explain
  581.             }
  582.         print 'Status: %d %s' % (code, message)
  583.         print 'Content-Type: text/html'
  584.         print 'Content-Length: %d' % len(response)
  585.         print
  586.         sys.stdout.write(response)
  587.  
  588.     def handle_request(self, request_text = None):
  589.         """Handle a single XML-RPC request passed through a CGI post method.
  590.  
  591.         If no XML data is given then it is read from stdin. The resulting
  592.         XML-RPC response is printed to stdout along with the correct HTTP
  593.         headers.
  594.         """
  595.  
  596.         if request_text is None and \
  597.             os.environ.get('REQUEST_METHOD', None) == 'GET':
  598.             self.handle_get()
  599.         else:
  600.             # POST data is normally available through stdin
  601.             try:
  602.                 length = int(os.environ.get('CONTENT_LENGTH', None))
  603.             except (TypeError, ValueError):
  604.                 length = -1
  605.             if request_text is None:
  606.                 request_text = sys.stdin.read(length)
  607.  
  608.             self.handle_xmlrpc(request_text)
  609.  
  610. if __name__ == '__main__':
  611.     print 'Running XML-RPC server on port 8000'
  612.     server = SimpleXMLRPCServer(("localhost", 8000))
  613.     server.register_function(pow)
  614.     server.register_function(lambda x,y: x+y, 'add')
  615.     server.serve_forever()
  616.